home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1994 June: Reference Library / Dev.CD Jun 94.toast / Periodicals / develop / develop Issue 11 / develop 11 code / The NetWork Project / NetWork Programmer's Stuff / SchedulerUnit.inc.p < prev    next >
Encoding:
Text File  |  1992-07-15  |  20.8 KB  |  772 lines  |  [TEXT/MPS ]

  1. {=============================================================================}
  2. { SchedulerUnit unit }
  3.     {SchedulerUnit.inc. version 1.1-0E }
  4.     { Copyright © 1989-1991 The NetWork Project, StatLab Heidelberg
  5.     Copyright © 1989-1991 G. Sawitzki, StatLab Heidelberg }
  6.     
  7.     {$SETC debugging=false}
  8.  
  9. {=============================================================================}
  10. {Things to do
  11. Move MyAddr to library and rename
  12.  
  13. Reduce safety levels. There should be just two flags
  14.     debugging    used for low level debugging
  15.     paranoia    (foolproof, or some nicer name) Used to add additional checks 
  16.                 if a user calls a method direcly. Should not be necessary if
  17.                 the Scheduler does all the message handling.
  18.                 
  19. Debugger calls should be channelled via HandleError. HandleError needs an 
  20. improvement to make a fail-safe call to the debugger on all platforms/systems.}
  21. {=============================================================================}
  22. function MyAddr(var transport:TransportPtr):MsgAddr;
  23. var tempaddr:MsgAddr;
  24. begin
  25.     tempAddr:=NetWorkScheduler.MySelf;
  26.     if transport=nil then begin
  27.         transport:=NetWorkScheduler.MyTransport;
  28.     end else tempaddr.a:=Transport^.TransportAddr;
  29.     MyAddr:=tempaddr;
  30. end;
  31.  
  32. {$IFC MemberKludge}
  33. {the member function of MPW 3.2 may run into a bus error instead
  34.                             of reporting false when the first argument is not an object. If
  35.                             MemberKludge is true, special cautions are taken to avoid this
  36.                             problem}
  37. {dereference and test whether structure starts with a "magic word"}
  38. function MemberKludge(p: univ handle):boolean;
  39. type lp=^longint;
  40. begin
  41.     MemberKludge:=(lp(ord4(p^)+2)^=longint('tMHd'));    
  42. end;
  43. {$ENDC}
  44.  
  45.  
  46. {$IFC UNDEFINED UsingEvents}
  47. {from event -- don't want quickdraw just to get a constant or the time}
  48. const NetWorkEvt = 10;
  49. FUNCTION TickCount: LONGINT;
  50. INLINE $A975;
  51. {$ENDC}
  52.  
  53.  
  54. {$IFC UNDEFINED UsingPackages}
  55. {instead of packages -- we do not want a package just to convert a number}
  56. procedure numtostring (l : longint; var s : str15);
  57. var k : longint; 
  58.     minus : boolean; { to be done }
  59. begin
  60.     if l=0 then s:='0' else begin 
  61.         s := '';
  62.         if l<0 then begin minus:=true; l:=-l end else minus:=false;
  63.         while (l > 0) do begin
  64.             k := l div 10;
  65.             l := l - k * 10;
  66.             insert (' ',s,1); s [1] := chr (l+ord ('0'));
  67.             l := k;
  68.         end;
  69.         if minus then insert ('-',s,1); 
  70.     end;
  71.     {        while length (s) < 8 do insert (' ', s, 1);}
  72. end;
  73. {$ENDC}
  74.  
  75.  
  76. {=============================================================================}
  77. {    Default task handler    }
  78.  
  79. procedure tMessageHandler.init;
  80. begin
  81. {$IFC MemberKludge}
  82.         MemberKludgeMagic:=longint('tMHd');
  83.         {$IFC paranoia}
  84.         if longint(@MemberKludgeMagic)-longint(handle(self)^)<>2 then ProgramBreak('object header size <>2');
  85.         {$ENDC}
  86. {$ENDC}
  87.  
  88.     NrPendingMessages:=0;
  89.     restart;
  90. end;
  91.  
  92. procedure tMessageHandler.restart;
  93. begin
  94.     contextstamp:=0;
  95. end;
  96.  
  97. procedure tMessageHandler.Stamp(Msg:MsgPtr);
  98.     {should be called only from MsgHeaderUsable (receiver's side) 
  99.     or from NewTask (generator's side). Should be called only once per message.}
  100. begin
  101.     with msg^ do if MsgUserRefCon<>longint(self) then {not yet stamped}
  102.     begin
  103.         {$IFC paranoia} if MsgUserRefCon<>longint(nil) then ProgramBreak('Stamp: got Msg with ref');{should not happen}
  104.         {$ENDC}
  105.  
  106.         NrPendingMessages:=NrPendingMessages+1;
  107.         Msg^.MsgUserRefCon:=longint(self);
  108.     end;
  109. end;
  110.  
  111. function tMessageHandler.Destroy(var Msg:MsgPtr):OsErr;
  112. begin
  113.     Destroy:=NoErr;    {still to decide: which error handling should be generic ?}
  114.     if Msg<>nil then  
  115.     begin
  116.         if Msg^.MsgUserRefCon=longint(self) then 
  117.         begin
  118.             Destroy:=DestroyMsg(Msg);
  119.             NrPendingMessages:=NrPendingMessages-1;
  120.             Msg:=nil;
  121.         end
  122.         {$IFC paranoia}else ProgramBreak('tMessageHandler.Destroy: not my message')    {$ENDC};
  123.     end
  124.     {$IFC paranoia} else ProgramBreak('tMessageHandler.Destroy(nil)')    {$ENDC};{Msg<>nil }
  125. end;
  126.  
  127. procedure tMessageHandler.DisposPrioPtr(var PrioPtr:UNIV Ptr);
  128. begin
  129.     if PrioPtr<>nil then begin {still to decide: check here ?}
  130.         DisposPtr(PrioPtr);
  131.         PrioPtr:=nil;
  132.     end;
  133. end;
  134.  
  135. procedure tMessageHandler.DisposCorePtr(var CorePtr:UNIV Ptr);
  136. begin
  137.     if CorePtr<>nil then begin {still to decide: check here ?}
  138.         DisposPtr(CorePtr);
  139.         CorePtr:=nil;
  140.     end;
  141. end;
  142.  
  143. function tMessageHandler.DisposMsg(var Msg:MsgPtr):OsErr;
  144.     {Should release all buffers associated with Msg,
  145.     and call Destroy}
  146. var tempMsg:MsgRec;
  147. begin
  148.     {override by releasing buffers first, if appropriate}
  149.     DisposMsg:=NoErr;
  150.     if Msg<>nil then  begin
  151.         tempMsg:= Msg^;
  152.         DisposMsg:=Destroy(Msg);{make sure that the buffers are not accessed any more}
  153.         with tempMsg do begin
  154.             if MsgPrioPtr<>nil then DisposPrioPtr(MsgPrioPtr);
  155.             if MsgCorePtr<>nil then DisposCorePtr(MsgCorePtr);
  156.         end;
  157.     end
  158.     {$IFC debugging} else debugstr('tMessageHandler.DisposeMsg(nil)'){$ENDC};{Msg<>nil }
  159.  
  160. end;
  161.  
  162.  
  163. function tMessageHandler.NewPrioPtr(var PrioSize:longint):ptr;
  164.                     {Allocate a new buffer for priority data. 
  165.                     Entry: PrioSize=Requested size;
  166.                     Exit: PrioSize=Allocated size;}
  167. var p:ptr;                    
  168. begin
  169.     if PrioSize=0 then NewPrioPtr:=nil
  170.     else begin
  171.     p:=NewPtr(Priosize);
  172.     if p=nil then PrioSize:=0;
  173.     NewPrioPtr:=p;    
  174.     end;
  175. end;
  176.  
  177. function tMessageHandler.NewCorePtr(var CoreSize:longint):ptr;
  178.                     {Allocate a new buffer for core=Core data. 
  179.                     Entry: PrioSize=Requested size;
  180.                     Exit: PrioSize=Allocated size;}
  181. var p:ptr;                    
  182. begin
  183.     if CoreSize=0 then NewCorePtr:=nil
  184.     else begin
  185.     p:=NewPtr(CoreSize);
  186.     if p=nil then CoreSize:=0;
  187.     NewCorePtr:=p;    
  188.     end;
  189. end;
  190.  
  191. {=============================================================================}
  192. {    prototype message handlers. receiving part    }
  193.  
  194.  
  195. {--------- This function sometimes will be customized ---------}
  196.  
  197. function tTaskHandler.MsgHeaderUsable(var msg:MsgPtr):boolean;
  198. {do a pre-check of an incoming message.}
  199. var MasterOk,ContextOk:boolean;
  200.     tempPrivilegedAddr:        MsgAddr;
  201. begin
  202.  
  203.     {$ifc debugging} if spare then debugstr('tTaskHandler.MsgHeaderusable check context;g'); {$endc}
  204.     ContextOk:=(ContextStamp=0) | (msg^.MsgReference=contextstamp);
  205.  
  206.     tempPrivilegedAddr:=PrivilegedAddr;{avoid $H-}
  207.     MasterOk:=(PrivilegedTimeout = 0) | (EqAddr(msg^.MsgSource,tempPrivilegedAddr));
  208.  
  209.  
  210.     If (PrivilegedTimeout <> 0) & MasterOk then 
  211.     PrivilegedTimeout:=tickCount+PrivilegedInterval;
  212.  
  213.     if (not MasterOk) & (PrivilegedTimeout<TickCount) then begin
  214.         PrivilegedTimeout:=0;  
  215.         MasterOk:=true;
  216.         {reset context, if context was master dependent.}
  217.     end;
  218.  
  219.     MsgHeaderUsable:=ContextOk&MasterOk;
  220.  
  221. end;
  222.  
  223.  
  224. {--------- These function generally will be customized ---------}
  225. function tTaskHandler.MsgUsable(var msg:MsgPtr): boolean;
  226. begin
  227.     MsgUsable:=true;
  228. end;
  229.  
  230.  
  231. {--------- This function must be customized -------------------------}
  232. {Keep in mind to call WaitNextEvent regularly if the message evaluation
  233. takes time.}
  234.  
  235. procedure tTaskHandler.MsgEvaluation(var msg:MsgPtr);
  236. begin
  237.  
  238. end;
  239.  
  240.  
  241. {prototype message handlers. receiving part}
  242.  
  243. procedure tTaskHandler.init;
  244. const
  245.     cPrivilegedInterval=60;    {ticks before feeling free for a new partner}
  246. begin
  247.     inherited init;        {will call restart}
  248.     PrivilegedInterval    :=cPrivilegedInterval;
  249. end;
  250.  
  251. procedure tTaskHandler.restart;
  252. begin
  253.     inherited restart;
  254.     ContextStamp:=0;
  255.     PrivilegedTimeout:=0;                    {now}
  256.     with PrivilegedAddr do 
  257.     begin a:=0;p:=longint('????'); end;        {local, anonymous}
  258.     UsableCapas:=cAnyCapas;
  259. end;
  260.  
  261.  
  262.  
  263. {=============================================================================}
  264. {    prototype message handlers. sending part    }
  265.  
  266. procedure tTaskGenerator.init;
  267. const    
  268.     cTickleInterval=60;    {ticks before trying a new partner. set by needsattention}
  269.     cWaitInterval=30;        {min interval between tasks, in ticks. set by newtask}
  270. begin
  271.  
  272.     {$IFC debugging} if spare then debugstr('start tTaskGenerator.init;g'); {$ENDC}
  273.  
  274.     inherited init;
  275.     TickleInterval:=cTickleInterval;
  276.     Waitinterval:=cWaitInterval;
  277. end;
  278.  
  279. procedure tTaskGenerator.restart;
  280. begin
  281.     {$IFC debugging} if spare then debugstr('start tTaskGenerator.start;g'); {$ENDC}
  282.     inherited restart;
  283.     contextstamp:=TimeStamp;
  284. end;
  285.  
  286. procedure tTaskGenerator.Stamp(Msg:MsgPtr); 
  287. begin
  288.     inherited Stamp(Msg);
  289.     Msg^.MsgReference:=ContextStamp;
  290. end;
  291.  
  292. function tTaskGenerator.NewTask(var Msg:MsgPtr):boolean;
  293. begin
  294.     if Msg<>nil then {set all task generator specific defaults}
  295.     with Msg^ do begin
  296.  
  297.         MsgCapasVerb:=DefaultCapasVerb;
  298.         MsgReference:=ContextStamp;
  299.         {$IFC paranoia}    {scheduler sets all fields to zero}
  300.         MsgUserRefCon:=longint(nil);
  301.         MsgPrioSize:=0;
  302.         MsgCoreSize:=0;
  303.         MsgPrioPtr:=nil;
  304.         MsgCorePtr:=nil;
  305.         {$ENDC}
  306.     end;
  307.     NewTask:=false; {but mark as useless so far}
  308. end;
  309.  
  310.  
  311. {=============================================================================}
  312.  
  313. {Scheduler implementation}
  314.  
  315. procedure tScheduler.sendmessage(msg:MessagePtr);
  316.     {send the message indicated by msg. could be replaced by a direct call to
  317.     the message system. is kept here only for easier tracing.}
  318. var newMsg:MsgPtr;
  319. var s:str255;
  320. begin
  321.     {$IFC debugging} if spare then begin MsgToString(msg,s);debugstr(concat('tScheduler.sendmessage ',s));end; {$ENDC}
  322.     with msg^ do begin
  323.         PrevDest:=msgDest;
  324.         msgSource:=MyAddr(MsgTrpPtr){MySelf; }{••• should go to msg system}
  325.     end;
  326.     HandleError(pSendMessage,SendMsg(msg,newmsg));
  327. end;
  328.  
  329. procedure tScheduler.replymessage(msg:MessagePtr;flagsToAdd:longint);
  330.     {send the message indicated by msg, but to msg
  331.     target}
  332.     
  333. var 
  334.     tempMsg:MsgRec;
  335.     newmsg:MsgPtr;
  336.     ttrick:record 
  337.         case boolean of
  338.         true:(a,b:integer);
  339.         false:(l:longint)
  340.     end;
  341.     tempstr:str255;
  342. begin
  343.     tempMsg:=msg^; {needed: MsgReference, MsgTrpPtr,… still to specify}
  344.     with tempmsg do 
  345.     begin
  346.         MsgDest:=MsgReply;
  347.         {MsgReply:=MySelf; -- no complaints &results should keep at reply addr.
  348.                     still to discuss: pipeline versus master-slaves}
  349.         MsgCapasVerb:=bor(bor(MsgCapasVerb,cMsgReply),flagsToAdd);
  350.     end;
  351.     {$IFC false}
  352.     msgToString(@tempMsg,tempstr);
  353.     logstrtime(concat('reply ',tempstr));
  354.     {$ENDC}
  355.     with tempMsg do begin
  356.         msgSource:=MyAddr(MsgTrpPtr); {••• should go to msg system}
  357.     end;
  358.     {$IFC debugging} if spare then begin MsgToString(msg,s);debugstr(concat('tScheduler.replymessage ',s));end; {$ENDC}
  359.  
  360.     HandleError(pReplyMessage,SendMsg(@tempMsg,newmsg));
  361. end;
  362.  
  363. procedure tScheduler.init;
  364. var tempstr:str255;tempaddr:MsgAddr;
  365. begin
  366.     {$IFC debugging} if spare then debugstr('start tScheduler.init;g'); {$ENDC}
  367.  
  368.     mySelf:=GetNetWorkAddr;
  369.     MyTransport:=nil;
  370.     
  371.     tempAddr:=MySelf;
  372.     addrToString(tempAddr,tempstr);
  373.     logstring(concat('tScheduler.init ',tempstr));
  374.  
  375.     TaskHandler:=nil;
  376.     TaskGenerator:=nil;
  377.     CoHandler:=Nil;
  378.  
  379.     ErrQuiet:=true;
  380.     Err:=NoErr;
  381.     reset;
  382.  
  383.     {this should be done only on send}
  384.     if Master then if Err=NoErr then HandleError(pInit,NlStart);
  385.     ErrQuiet:=false;
  386. end;
  387.  
  388. procedure tScheduler.reset;
  389. begin
  390.     {$IFC debugging} if spare then debugstr('start tScheduler.reset;sc;g'); {$ENDC}
  391.  
  392.     {if sending then HandleError(pUndefined,nlStop);}
  393.  
  394.     {HandleError(pUndefined,flushMsg); }{??}
  395.  
  396.     sending:=false;
  397.     receiving:=false;
  398.     PrevDest:=MySelf;    {will in general only define the type}
  399.     PrevDest.a:=NLNext(PrevDest.a);
  400.     TaskAddr:=MySelf;    {??????????}
  401.     if TaskHandler<>nil then TaskHandler.Restart;
  402.     if TaskGenerator<>nil then TaskGenerator.Restart;
  403.     if Cohandler<>nil then CoHandler.Reset;
  404.     Err:=NoErr;
  405.     {HandleError(pUndefined,flushMsg);}{would cancel launch message as well}
  406. end;
  407.  
  408. procedure tScheduler.free; override;
  409. begin
  410.     {$IFC debugging} if spare then debugstr('start tScheduler.free;g');{$ENDC}
  411.     reset;
  412.     if TaskHandler<>nil then TaskHandler.free;
  413.     if TaskGenerator<>nil then TaskGenerator.free;
  414.  
  415.     ErrQuiet:=true;    {••• do not report errors on uninstalling. Should we ??}
  416.     inherited free;
  417. end;
  418.  
  419. procedure tScheduler.setSending(onoff:boolean);
  420. begin
  421.     if onOff then begin
  422.         if (TaskHandler<>nil) & (Err=NoErr) then sending:=true 
  423.     end
  424.     else sending:=false;
  425. end;
  426.  
  427. procedure tScheduler.setReceiving(onoff:boolean);
  428. begin
  429.     if onOff then  begin
  430.         if (TaskGenerator<>nil) & (Err=NoErr) then Receiving:=true 
  431.     end
  432.     else Receiving:=false;
  433. end;
  434.  
  435. Procedure tScheduler.InitTaskHandler(newTaskHandler:tTaskHandler);
  436. begin
  437.     {$IFC debugging}     if spare then debugstr('start tScheduler.InitTaskHandler;g'); {$ENDC}
  438.     TaskHandler:=newTaskHandler;
  439.     if TaskHandler=nil then  HandleError(pUndefined,cNilError)
  440.     else begin
  441.         {set tScheduler proposals, if any}
  442.         TaskHandler.init;
  443.     end;
  444.     Receiving:=(TaskHandler<>nil);
  445.  
  446.     {•••••should  clear pending messages.}
  447.     {$IFC debugging} if receiving & spare then debugstr('tScheduler.InitTaskHandler receiving;g'); {$ENDC}
  448.  
  449. end;
  450.  
  451.  
  452. Procedure tScheduler.InitTaskGenerator(newTaskGenerator:tTaskGenerator);
  453. begin
  454.     {$IFC debugging}     if spare then debugstr('start tScheduler.initTaskGenerator;g');     {$ENDC}
  455.     {HandleError(pUndefined,nlStart);} {should really go here. But to allow for jump starts…}
  456.     TaskId:=TimeStamp;    { a random proposal}
  457.     TaskIterations:=maxint;        {we want to do binary search from here}
  458.  
  459.     TaskGenerator:=newTaskGenerator;
  460.     if TaskGenerator=nil then HandleError(pUndefined,cNilError) else
  461.     begin
  462.         with TaskGenerator do         {set tScheduler proposals, if any}
  463.         begin
  464.             DefaultCapasVerb:=cAnyCapas;
  465.         end;
  466.         TaskGenerator.init;
  467.         with TaskGenerator do
  468.         begin
  469.             NextTickle:=tickCount+TickleInterval;
  470.             NextWait:=tickCount+WaitInterval;
  471.         end;
  472.     end;
  473.     sending:=(TaskGenerator<>nil);
  474.     {$IFC debugging} if spare then debugstr('stop tScheduler.initTaskGenerator;g');{$ENDC}
  475. end;
  476.  
  477. procedure tScheduler.DoNewTask(addr:MsgAddr;Transport:TransportPtr);
  478. var tempMsgPtr:MsgPtr;
  479. begin
  480.     tempMsgPtr:=MsgPtr(NewPtrClear(sizeof(MsgRec)));    {misuse. we only want a clean variable}
  481.     {is there a proper way to clear a variable, without bothering the memory manager ? 
  482.     Fillchar could be misused here, but this would be implementation dependent }
  483.     
  484.     if tempMsgPtr<>nil then begin
  485.         with tempMsgPtr^ do begin {fill in the fields the scheduler knows about}
  486.         MsgSource:=MyAddr(Transport);{MySelf;}
  487.         MsgDest:=addr;
  488.         MsgReply:=MyAddr(Transport);{MySelf;}
  489.         MsgTrpPtr:=Transport;
  490.     end;
  491.         TaskAddr:=addr; {note address for cohandler}
  492.         {***}                  if CoHandler<>nil then CoHandler.Cohandle(pStartNewTask,tempMsgPtr);
  493.         if TaskGenerator.NewTask(tempMsgPtr) then begin
  494.             PreventIdle; { added 4/15/90 -- Joachim }
  495.             {***}                  if CoHandler<>nil then CoHandler.Cohandle(pNewTaskDone,tempMsgPtr);
  496.             SendMessage(tempMsgPtr);
  497.         NextWait:=tickCount+TaskGenerator.WaitInterval;{block timer}
  498.     end
  499.     else begin 
  500.  
  501.             {***}                  if CoHandler<>nil then CoHandler.Cohandle(pNoNewTask,tempMsgPtr);
  502.         NextWait:=0;{release timer}
  503.     end;
  504.         DisposPtr(Ptr(tempMsgPtr));
  505.     end;
  506. end;
  507.  
  508. {---------------------------------------------------------------}
  509. {    *****************•••••••••••••••••****************    }
  510.  
  511.  
  512. procedure tScheduler.HandleMsg (Msg : MsgPtr);
  513. var i : integer; p : Ptr; 
  514.     listenSize:longint;listenHandler:tTaskHandler;
  515.     myErr:OsErr;
  516.  
  517.     function killMsg:osErr;
  518. var s:str255;
  519. begin
  520.  
  521.     with Msg^ do
  522.     begin 
  523.         {$IFC debugging}
  524.         if (ptr(MsgUserRefCon)<>nil) then begin
  525.             MsgToString(msg,s);
  526.             LogString(concat('killmsg ',s));
  527.             debugstr(concat(s,';g'));
  528.         end;
  529.         {$ENDC}
  530.         {$IFC true}
  531.  
  532.         if (ptr(MsgUserRefCon)<>nil) & 
  533.         {$IFC MemberKludge}
  534.         MemberKludge(MsgUserRefCon) &
  535.         {$ENDC}
  536.         member(tObject(MsgUserRefCon),tMessageHandler) then begin
  537.  
  538.             killMsg:=tMessageHandler(MsgUserRefCon).DisposMsg(msg) ;
  539.             MsgUserRefCon:=longint(nil);
  540.         end else
  541.         {$ENDC}
  542.         killMsg:= DestroyMsg (Msg);
  543.     end;
  544.  
  545. end;
  546.  
  547. function ReceiveMsg:OsErr;
  548. var buffer : Ptr;
  549.     ReceiveSize:longint;
  550. begin
  551.     ReceiveSize:=Msg^.MsgCoreSize;
  552.     if ReceiveSize=0 then ReceiveMsg:=AcceptMsg(Msg,Nil,0) else 
  553.     begin
  554.         with Msg^ do begin
  555.         buffer:=Msg^.MsgCorePtr;    {the taskhandler should have installed it}
  556.         if buffer=nil then    {no buffer: try to create it}
  557.         buffer:=tTaskHandler(MsgUserRefCon).NewCorePtr(ReceiveSize);
  558.         end;
  559.  
  560.         if buffer = nil then ReceiveMsg:=cNilError
  561.         else ReceiveMsg:= AcceptMsg (Msg, buffer, ReceiveSize);
  562.     end;
  563. end; {ReceiveMsg}
  564.  
  565. begin
  566.     if Msg<>nil then with Msg^ do 
  567.     if (MsgResult < 0) | (BAnd (MsgCmd, tMinorMask) >= tTimeout) then 
  568.     CheckError('Handle bad msg',killMsg )
  569.     else case BAnd (MsgCmd, tMajorMask) of
  570.         tListen : begin 
  571.             {$IFC paranoia}
  572.             if MsgUserRefCon<>longint(nil) then ProgramBreak('handleMsg: got Msg with ref');{should not happen}
  573.             {$ENDC}
  574.             if not TaskHandler.MsgHeaderUsable(Msg) then 
  575.             CheckError ('Out of context', killMsg)
  576.             else begin
  577.  
  578.                 if MsgUserRefCon=longint(nil) then {no special handler assigned}
  579.                 begin
  580.                     listenHandler:=taskhandler; {default to scheduler's task handler}
  581.                     listenHandler.stamp(msg);
  582.                 end 
  583.                 else 
  584.                 listenHandler:=tTaskHandler(MsgUserRefCon); {take special handler}
  585.                 listenSize:=MsgPrioSize;
  586.                 p:= MsgPrioPtr;
  587.                 if (p=nil) then p:=listenhandler.NewPrioPtr(listenSize);    {fall back solution}
  588.                 if (p=nil) & (MsgPrioSize>0) then {in context, no buffer,but existing prio}
  589.                 CheckError('Empty Prio Buffer',cNilError)
  590.                 else begin
  591.                     CheckError('GetMsg',GetMsg(Msg,p,listenSize));
  592.                     TaskAddr:=msg^.MsgSource;
  593.                     if listenHandler.MsgUsable(msg) then begin
  594.                         myErr:=ReceiveMsg;
  595.                         {***}                  if CoHandler<>nil then CoHandler.Cohandle(pUsable,msg);
  596.  
  597.                         if myErr<>noErr then 
  598.                         begin
  599.                             CheckError ('Receive',myerr);
  600.                             CheckError ('Deny', killMsg);
  601.                         end;
  602.                     end
  603.  
  604.                     else 
  605.                     begin 
  606.                     if CoHandler<>nil then CoHandler.Cohandle(pUnUsable,msg);
  607.                     CheckError ('Deny', killMsg);
  608.                     end;
  609.                 end;
  610.             end;
  611.         end;
  612.         tAccept  : begin
  613.             tTaskHandler(MsgUserRefCon).MsgEvaluation(Msg);
  614.             CheckError ('Dispos accepted',killMsg);
  615.         end;
  616.         tPost : CheckError ('Posted',KillMsg);
  617.         otherwise ProgramBreak ('tScheduler.HandleMsg:unexpected msgCmd');
  618.     end;
  619. end;
  620.  
  621.  
  622. {---------------------------------------------------------------}
  623. {        }
  624.  
  625. procedure tScheduler.PeriodicTask;
  626. const timetospend=10;{ticks allowed for receive}
  627. var 
  628.     timeout:longint;
  629.     destinationaddr:MsgAddr;
  630.     TaskGeneratorNeedsAttention:boolean;
  631. begin
  632.  
  633.     {make sure handlers are installed. needed for Housekeeping ? else later}
  634.     if TaskHandler=nil then receiving:=false;
  635.     if TaskGenerator=nil then sending:=false;
  636.  
  637.     if sending  then 
  638.     begin 
  639.         TaskGeneratorNeedsAttention:=false;
  640.         destinationaddr:=PrevDest;
  641.         if (tickCount>NextTickle)  then begin
  642.             {$IFC debugging} if spare then debugstr('TaskGeneratorNeedsAttention;g');{$ENDC}
  643.             NextTickle:=tickCount+TaskGenerator.TickleInterval;
  644.             {NextWait is handled by DoNewTask}
  645.             destinationaddr.a:=NLRandom;
  646.             TaskGeneratorNeedsAttention:=true;
  647.         end else if (tickCount>NextWait) then begin 
  648.             destinationaddr.a:=NLNext(destinationaddr.a);
  649.             TaskGeneratorNeedsAttention:=true;
  650.         end;
  651.         if TaskGeneratorNeedsAttention then begin
  652.             DoNewTask(destinationaddr,MyTransport);
  653.         end;
  654.     end;{TaskGeneratorNeedsAttention}
  655. end;
  656.  
  657. function tScheduler.GetSleep:longint;
  658. var tempSleep,now,nexttime:longint;
  659. begin
  660.     tempSleep := maxlongint; { default: no time required at all }
  661.     now:=tickcount;
  662.     if sending then begin nexttime:=NextWait;if nextTickle<nexttime then nexttime:=nexttickle; end;
  663.     tempSleep:=nexttime-now;    
  664.     if tempSleep < 0 then tempSleep := 0;
  665.     GetSleep:=tempsleep;
  666. end;
  667.  
  668.  
  669. procedure tscheduler.kickOff(maxcount,maxticks:integer);
  670. var 
  671.     {$IFC debugging}
  672.  
  673.     s,s1:str255;
  674.     oldmaxcount,oldticks:longint;
  675.     {$ENDC}
  676.     timelimit:longint;
  677. begin
  678.     {$IFC debugging}
  679.     oldmaxcount:=maxcount;
  680.     oldticks:=maxticks;
  681.     {$ENDC}
  682.  
  683.     timelimit:=tickcount+maxticks;
  684.     if maxcount=0 then maxcount:=NLCount;{ we will do at least one}
  685.     repeat 
  686.         nextTickle:=0;    
  687.         nextWait:=0;
  688.         PeriodicTask;
  689.         maxcount:=maxcount-1;
  690.     until     (not (sending | receiving))|(tickcount>timelimit)|(maxcount<=0);
  691.     {$IFC debugging}     oldTicks:=tickCount-oldticks;        {time done }
  692.     oldmaxcount:=oldmaxcount-maxcount;    {counts done}
  693.     if spare then ProgramBreak('tscheduler.kickOff tickle done.;dm a6 '); {$ENDC}
  694.  
  695. end;
  696.  
  697. procedure tScheduler.handleError(from:tSchedulerPhase;which:OsErr);
  698. label 1,9;
  699. const
  700.     _Unimplemented                     = $A89F;
  701.     _Debugger                         = $A9FF;
  702.     _DebugStr                         = $ABFF;
  703.  
  704. var s:str255;s1,s2:str15;
  705. begin
  706.     {filter bogus errors}
  707.     if which <> noerr then 
  708.     case from of 
  709.         pSendMessage: case which of
  710.             eQueEmpty,{may last until timeout -- note while stage alpha. needs
  711.             less tasks or more message buffers}
  712.             ePrio2Big,
  713.             eSizeLimit,
  714.             eProtType,
  715.             eTransportDown,
  716.             eCmdSequence:;
  717.             otherwise which:=noErr
  718.         end;
  719.         pHousekeepingDestroy,pFree,pAcceptMsg: which:=noErr;
  720.         pinit:;{note all errors}
  721.  
  722.         otherwise
  723.     end;    
  724.  
  725.  
  726.     1:    if  (which<>noErr) 
  727.     & (which<>eInvalid) { for now. network error handling still to be fixed •••}
  728.     then begin
  729.  
  730.         if (not ErrQuiet) & {some debugger installed}
  731.         (NGetTrapAddress (_DebugStr, ToolTrap) <> NGetTrapAddress (_Unimplemented, ToolTrap))
  732.         then {report the error, using the debugger} begin
  733.             numtostring(which,s1);
  734.             numtostring(ord(from),s2);
  735.             s:=concat('tScheduler detected error ',s1,' from ',s2,';sc');
  736.             debugstr(s);    {debugger esists}
  737.             if spare then begin debugstr('tScheduler.handleError going to clear error code.');end;{spare handleerror}
  738.  
  739.         end;
  740.         Err:=which;
  741.  
  742.         if spare then begin err:=NoErr;end;{spare handleerror}
  743.  
  744.         ErrFrom:=from;
  745.     end; {Err}
  746.     9:
  747. end;
  748.  
  749.  
  750.  
  751. {=============================================================================}
  752.     {    cohandler implementation    
  753.     There is no real default implementation for cohandlers. This is just a 
  754.     prototype to define the calling conventions.}
  755.  
  756. procedure tSchedulerCohandler.CoHandle(cmd:tSchedulerPhase;msg:MsgPtr);
  757. begin
  758.     case cmd of
  759.         pUndefined:;
  760.         pUsable:;
  761.         pUnUsable:;
  762.         pStartNewTask:;
  763.         pNewTaskDone:;
  764.         pNoNewTask:;
  765.         otherwise
  766.     end;
  767. end;
  768.  
  769. procedure tSchedulerCohandler.Reset;
  770. begin
  771. end;
  772.